#include "AVCodecContextPP.hpp"

AVCodecContextPP::AVCodecContextPP()
{
//    codecctx = avcodec_alloc_context3(NULL);
}

AVCodecContextPP::~AVCodecContextPP()
{
    if (b_new)
    {
        return;
    }
    if (codecctx!=nullptr)
    {
        avcodec_close(codecctx);
        avcodec_free_context(&codecctx);
    }
}

int AVCodecContextPP::SetDecodecCtx(AVCodecParameters * _par)
{
    if (_par==nullptr)
    {
        return -1;
    }
    if (!b_new)
    {
        return -1;
    }
    codecctx = avcodec_alloc_context3(avcodec_find_decoder(_par->codec_id)); //解码
    if (!codecctx)
    {
        return -1;
    }
    b_new = false;
    if (avcodec_parameters_to_context(codecctx, _par)<0)
    {
        avcodec_free_context(&codecctx);
        return -1;
    }
    if (avcodec_open2(codecctx, codecctx->codec, NULL) < 0) //AVDictionary 可以用于微调 
    {
        avcodec_free_context(&codecctx);
        return -1;
    }
    b_new = false;
    return 0;
}

int AVCodecContextPP::SetEncodecCtx(AVCodecParameters * _par) //编解码器的各项参数可以继续细调
{
    if (_par == NULL)//c
    {
        return -1;
    }
    if (!b_new)
    {
        return -1;
    }
    AVCodec *codec = avcodec_find_encoder(_par->codec_id);
    codecctx = avcodec_alloc_context3(codec); //参数NULL也行
    if (!codecctx)
    {
        return -1;
    }
    codecctx->codec = codec;
    if (avcodec_parameters_to_context(codecctx, _par)<0)
    {
        avcodec_free_context(&codecctx);
        return -1;
    }
    if (codecctx->codec_type == AVMEDIA_TYPE_VIDEO)
    {
        codecctx->time_base = { 1,100000 };
        if (codecctx->codec_id == AV_CODEC_ID_H264)
        {
            codecctx->bit_rate_tolerance = 0;
            codecctx->ticks_per_frame = 2;
        }
        if (codecctx->codec_id == AV_CODEC_ID_HEVC)
        {
            codecctx->bit_rate_tolerance = 0;
            codecctx->ticks_per_frame = 1;
        }
    }
    else if (codecctx->codec_type == AVMEDIA_TYPE_AUDIO)
    {
    }
    //视频的编码参数还要再研究效果和反应 只设置码率是不够的  算子和清晰度或画质层级都要设置   画面还原的效果好过头，码率估计超标了，可以稍微调整一下 调低一点都行
    codecctx->qmax = 51; //这些参数要么写死要么手动 先写死
    codecctx->qmin = 2;
//    codecctx->qblur = 0.0;

//    codecctx->me_range = 16; //
    codecctx->max_qdiff = 4;
//    codecctx->qcompress = 0.6;
    codecctx->gop_size = 12;
//    codecctx->b_quant_offset = 1.25;
//    codecctx->spatial_cplx_masking = 0.3;
    codecctx->lmax = 5;
    codecctx->lmin = 1;

//    codecctx->lumi_masking = 0.0;
//    codecctx->dark_masking = 0.0;

//    codecctx->i_quant_factor = -0.8;
//    codecctx->me_subpel_quality = 8;

    //codecctx->intra_quant_bias = 999999;
    //codecctx->inter_quant_bias = 999999;

    codecctx->mb_lmin = 236;
    codecctx->mb_lmax = 3658;

//    codecctx->me_penalty_compensation = 256; // @deprecated use encoder private options instead

//    codecctx->bidir_refine = 1;

    codecctx->keyint_min = 5;
//    codecctx->scenechange_factor = 6; //deprecated
    codecctx->mv0_threshold = 256;
//    codecctx->b_sensitivity

//    av_opt_set(codecctx->priv_data, "preset", "slow", 0);
    if (avcodec_open2(codecctx, codec, NULL) < 0) //AVDictionary 可以用于微调 
    {
        return -1;
    }
    codecctx->flags |= CODEC_FLAG_GLOBAL_HEADER;
    b_new = false;
    return 0;
}

int AVCodecContextPP::DecodecPacket(AVPacket * _packet, AVFrame * _frame)
{
    if (avcodec_send_packet(codecctx, _packet) != 0)
    {
        PrintLog("Failed avcodec_send_packet. INDEX_990");
        return 1; //这里的返回区分一下  表明不同状态
    }
    int ret;
    ret = avcodec_receive_frame(codecctx, _frame);
    if (ret != 0)
    {
        PrintLog("Failed avcodec_receive_frame. INDEX_991");
        return 2;
//        return ret;//receive的返回值较复杂
    }
    return 0;
}

int AVCodecContextPP::EncodecFrame(AVFrame * _frame, AVPacket * _packet)
{
    av_packet_unref(_packet);
    if (avcodec_send_frame(codecctx, _frame) != 0)
    {
        return 1;
    }
    if (avcodec_receive_packet(codecctx, _packet) != 0)
    {
        return 2;
    }
    return 0;
}
